
import os
import sys
import wx

import ab_environ
import ab_dialog
import ab_frame
import ab_info
import ab_loading
import ab_logging
import ab_plugins
import ab_preferences_dialog
import ab_utils

import ab_ids as ai

import ab_asciitable
import ab_plx_viewer
import ab_plugin_info
import ab_full_editor

from ab_platform import Platform


class CoreApp(wx.App):
    # the value mapped the order in ab_dialog.SaveDialog
    SavedAsOriginal = 0x00
    SavedAsBinary = 0x01
    SavedAsAtrTree = 0x02

    def __init__(self, cf, open_files, recent_ids):
        self.frame = None
        self.handlers = dict()
        self.children = dict()

        self.recent_files = list()

        self.cf = cf
        self.open_files = open_files or list()
        self.recent_ids = recent_ids

        self.log = self.readKey(ai.CfgParaLogObject)
        self.plx = self.readKey(ai.CfgParaPlxObject)

        self.input_nb = 0

        # enable the lambda functions to predigit the functions
        # FIXME: remove the multiple definitions
        self.info = self.log.info
        self.warn = self.log.warn
        self.dbg = self.debug = self.log.debug
        self.critic = self.log.critic
        self.error = self.log.error

        self.debug('<CoreApp.__init__> Enter')

        # register the document applications
        self.registerHandler(ab_asciitable.AsciiTableDocHandler())
        self.registerHandler(ab_plugin_info.PlxInfoDocHandler())
        self.registerHandler(ab_plugin_info.PlxListDocHandler())
        self.registerHandler(ab_plx_viewer.FileViewerDocHandler())
        self.registerHandler(ab_plx_viewer.NormalViewerDocHandler())

        self.registerHandler(ab_full_editor.PlxFileViewerDocHandler())
        self.registerHandler(ab_full_editor.PlxEditorViewerDocHandler())

        if wx.VERSION[:2] > (2, 8):
            import ab_config_list
            self.registerHandler(ab_config_list.ConfigListDocFrameDocHandler())

            import ab_env_viewer
            self.registerHandler(ab_env_viewer.EnvListDocFrameDocHandler())

        self.init()
        self.loadRecentFiles()

        wx.App.__init__(self, 0)
        wx.EVT_ACTIVATE_APP(self, self.OnActivate)

        self.debug('<CoreApp.__init__> Exit')

    def OnInit(self):
        if wx.VERSION < (2, 9):
            wx.InitAllImageHandlers()

        self.cf.writeKey(ai.CfgParaDisplayFont,
                         self.cf.readKey(ai.CfgParaPlatform).getFont())

        # Initialize the main frame
        self.frame = ab_frame.CoreMdiFrame(self)
        self.frame.Show(True)
        self.SetTopWindow(self.frame)

        # handle the options
        for ii in self.recent_ids or list():
            try:
                key = int(ii) - 1
                if key >= 0:
                    self.open_files.append(self.recent_files[key])
                else:
                    raise IndexError
            except ValueError:
                self.error('%s is not an integer' % ii)
            except IndexError:
                self.error('%s is out of recent file indice' % ii)

        for file_name in self.open_files or list():
            self.openDocument('viewer', file_name)

        return True

    def OnActivate(self, event):
        self.debug('<CoreApp.OnActivate> Enter')

        if event.GetActive():
            self.updatePlxState()

        self.debug('<CoreApp.OnActivate> Exit')

    def readKey(self, name, idx=None, *args, **kws):
        return self.cf.readKey(name, idx, *args, **kws)

    def writeKey(self, name, value, idx=None, *args, **kws):
        self.cf.writeKey(name, value, idx, *args, **kws)

    def removeKey(self, name, idx=None):
        self.cf.removeKey(name, idx)

    def getKeyType(self, name):
        return self.cf.getKeyType(name)

    def flushKey(self):
        self.cf.flush()

    def getWindow(self):
        return self.frame

    def init(self):
        env = self.readKey(ai.CfgParaEnvObject)
        env.set('APPLICATION', ab_info.getApplicationName())
        env.set('VERSION', ab_info.getApplicationVersionString())
        env.set('COPYRIHGT', ab_info.getApplitionCopyright())

    def loadRecentFiles(self):
        num = len(self.readKey(ai.CfgParaFileRecentOpened))
        for k in range(num):
            va = self.readKey(ai.CfgParaFileRecentOpened, k)
            if va is not None:
                self.recent_files.append(va)

    def updateRecentFiles(self, filename):
        num = self.readKey(ai.CfgParaDefaultRecNum)

        plat = self.cf.readKey(ai.CfgParaPlatform)
        for name in self.recent_files:
            if plat.getFileName(filename) == plat.getFileName(name):
                self.recent_files.remove(name)
                break

        self.recent_files.insert(0, filename)
        self.recent_files = self.recent_files[:num]

        self.removeKey(ai.CfgParaFileRecentOpened)
        for k, f in enumerate(self.recent_files):
            self.writeKey(ai.CfgParaFileRecentOpened, f, k)

    def getRecentFiles(self):
        return self.recent_files

    def registerHandler(self, handler):
        if handler.name in self.handlers:
            self.error('DocumentManager - "%s" is registered by handler %r' % (
                       handler.name, self.handlers[handler.name]))

        self.handlers[handler.name] = handler

    def openDocument(self, name, *args, **kws):
        handler = self.handlers.get(name)
        if handler is None:
            self.error("DocumentManager - %s isn't registered" % name)
            return False

        # use the first args to build up the new name to avoid duplication
        if len(args) > 0:
            name += '::%s' % self.cf.readKey(
                ai.CfgParaPlatform).getFileName(args[0])

        # check the handler relative attribute
        frame = self.children.get(name)
        if frame is not None:
            # don't prompt anything for internal document
            if frame.getHandler().isSingleton():
                frame.SetFocus()
                frame.Raise()
            else:
                wx.MessageBox('The file/window has been opened', 'Error',
                              wx.OK | wx.CENTRE | wx.ICON_ERROR)
                return False
        else:
            frame = handler.getFrame(self.frame, self, *args, **kws)
            if frame.isReady():
                frame.Show()
                self.children[name] = frame
                self.info('openDocument - %s added' % name)
                if handler.isUpdateToRecentMenu():
                    self.updateRecentFiles(frame.filename)
                    self.frame.updateRecentMenu()
            else:
                frame.Close()
                self.error('DocumentManager - <%r> is not ready' % frame)
                return False

        return True

    def closeDocument(self, name=None, trigger=False):
        if name is None:
            file_names = self.children.keys()
            for name in file_names:
                frame = self.children[name]
                # del self.children[name]
                frame.Close()
        else:
            if name not in self.children:
                for k, frame in self.children.items():
                    if frame.getTitle() == name and \
                            k.startswith(frame.getHandler().getName()):
                        name = k

            if name in self.children:
                frame = self.children[name]
                if trigger:
                    frame.Close()
                else:
                    del self.children[name]
                    self.info('closeDocument - %s removed' % name)
            else:
                self.error('closeDocument - %s is not closed' % name)

    def updateStatus(self, column, value):
        if self.frame is not None:
            self.frame.updateStatus(column, value)

    def clearStatus(self):
        if self.frame is not None:
            self.frame.clearStatus()

    def updatePlxState(self):
        state = False

        if self.readKey(ai.CfgParaReloadIndicator) and \
                not self.readKey(ai.CfgParaReloadSuppress):
            plx = self.readKey(ai.CfgParaPlxObject)

            update_list = plx.getModified()
            if len(update_list) > 0:
                if self.readKey(ai.CfgParaReloadBackground):
                    state = True
                else:
                    if len(update_list) > 1:
                        dialog = wx.MessageDialog(None,
                                                  'More than one plug-in is '
                                                  'update, reload them?',
                                                  'Reload',
                                                  wx.YES | wx.NO | wx.CENTRE |
                                                  wx.ICON_QUESTION)
                    else:
                        dialog = wx.MessageDialog(None, '%s is update, reload?'
                                                  % update_list[0], 'Reload',
                                                  wx.YES | wx.NO | wx.CENTRE |
                                                  wx.ICON_QUESTION)

                    state = dialog.ShowModal() == wx.ID_YES

                if state:
                    for plx_name in update_list:
                        ret, _ = plx.installFile(plx_name, tryrun=True)
                        if ret:
                            plx.installFile(plx_name)
                else:
                    self.writeKey(ai.CfgParaReloadSuppress, True)

        return state

    def getOpenWithPlx(self, parent):
        dialog = ab_dialog.OpenWithPlxDialog(parent, self, 'Open with Plug-in')
        if dialog.ShowModal() == wx.ID_OK:
            filename, plx, opaque = dialog.getValue()
            return True, filename, plx, opaque
        else:
            return False, '', '', None

    def getInputWithPlx(self, parent):
        self.input_nb += 1

        title = 'Input %d' % self.input_nb
        dialog = ab_dialog.OpenWithPlxEditorDialog(parent, self, title)
        if dialog.ShowModal() == wx.ID_OK:
            content, plx, opaque = dialog.getValue()
            return True, title, content, plx, opaque
        else:
            return False, title, '', '', None

    def getSaveState(self, parent, enabled_list=None):
        dialog = ab_dialog.SaveDialog(parent, self,
                                      'Select item to save',
                                      enabled_list or list())
        if dialog.ShowModal() == wx.ID_OK:
            return True, dialog.getValue()
        else:
            return False, 0

    def setPreferences(self, parent):
        dialog = ab_preferences_dialog.PreferencesDialog(parent, self)
        if dialog.ShowModal() == wx.ID_OK:
            dialog.save()

        dialog.Destroy()

    def getConfigure(self, parent, title, prompts, showbutton=False):
        dialog = ab_dialog.GeneratorPromptDialog(parent, self,
                                                 title, prompts, showbutton)
        dialog.CenterOnScreen()

        opaque = None
        ret = dialog.ShowModal()
        if not showbutton or (showbutton and ret == wx.ID_OK):
            opaque = dialog.getValues()

        dialog.Destroy()

        return opaque

    def getDumpParameters(self, parent, size=0, line_nb=16):
        dialog = ab_dialog.DumpDialog(parent, self, size, line_nb)
        if dialog.ShowModal() == wx.ID_OK:
            rets = dialog.getValues()
        else:
            rets = None

        return rets

    def getNewPosition(self, parent, size=1, curpos=0):
        dialog = ab_dialog.NewPositionDialog(parent, self, size, curpos)
        if dialog.ShowModal() == wx.ID_OK:
            ret = dialog.getValue()
        else:
            ret = None

        return ret

    def getChoice(self, parent, title, items):
        dialog = ab_dialog.ChoiceDialog(parent, self, title, items)
        if dialog.ShowModal() == wx.ID_OK:
            ret = dialog.getValue()
        else:
            ret = None

        return ret

    def showAbout(self, parent):
        ab_info.showAboutDialog(parent)


def main(cwp, cf, open_files, recent_ids, no_ui=False):
    hasConsole = ab_utils.withConsole(sys.argv[0])
    # set platform
    plat = Platform.getInstance()
    cf.writeKey(ai.CfgParaPlatform, plat)
    # set current working path
    cf.writeKey(ai.CfgParaCwd, plat.getFileName(cwp))
    # Object Console
    cf.writeKey(ai.CfgParaConsole, hasConsole)
    # Object Environ
    env = ab_environ.Environment()
    env.set('APP_PATH', cwp, 'str,dir')
    env.set('APP_DATA', plat.getDataDirectory(), 'str,dir')
    cf.writeKey(ai.CfgParaEnvObject, env)
    # Object Configure
    cf.writeKey(ai.CfgParaConfigObject, cf)
    # Object Logging
    logger = ab_logging.Logger(not hasConsole,
                               cf.readKey(ai.CfgParaFileLogName),
                               level=cf.readKey(ai.CfgParaVerbose))
    cf.writeKey(ai.CfgParaLogObject, logger)
    # Object Plug-in
    cf.writeKey(ai.CfgParaPlxObject, ab_plugins.PluginManager(cf))
    # clear restart flag
    cf.writeKey(ai.CfgParaAppRestart, False)

    if not no_ui:
        # load configurable service
        app_load = ab_loading.AppLoad(cf)
        app_load.MainLoop()

        # activate normal application
        app_main = CoreApp(cf, open_files, recent_ids)

        # load help object
        hlpObject = ab_info.HelpContent(app_main)
        cf.writeKey(ai.CfgParaHelpObject, hlpObject)
        app_main.MainLoop()

        # release the help object
        hlpObject.release()

    return cf.readKey(ai.CfgParaAppRestart)
